package main

import "fmt"

func main() {
	/*
		切片 slice
		append() 处理切片
		动态数组
		切片由三个部分组成：指向底层数组的指针，切片元素个数，切片的容量
		引用类型
		遍历方式和数组类似，通过 for 或者 for range 来遍历
	*/

	// 切片的定义
	// 不需要加长度，不然就是数组了
	var slice1 []int
	fmt.Printf("slice1的长度=%d,容量=%d,切片指向的底层数组的地址=%p,切片自己的地址=%p\n",
		len(slice1), cap(slice1), slice1, &slice1)
	// 定义一个数组
	array1 := [...]int{1, 2, 3, 4, 5}
	// 打印数组的内容
	for i := 0; i < len(array1); i++ {
		fmt.Printf("array1[%d]=%d, 地址=%p\n", i, array1[i], &array1[i])
		/*
			array1[0]=1, 地址=0xc0000ac060
			array1[1]=2, 地址=0xc0000ac068
			array1[2]=3, 地址=0xc0000ac070
			array1[3]=4, 地址=0xc0000ac078
			array1[4]=5, 地址=0xc0000ac080
		*/
	}

	// 切片去引用数组
	// 默认情况下切片的长度和容量是一致的
	// 引用之后，切片会指向数组的首地址
	slice1 = array1[:]
	for i := 0; i < len(slice1); i++ {
		fmt.Printf("slice1[%d]=%d, 地址=%p\n", i, slice1[i], &slice1[i])
		/*
			slice1[0]=1, 地址=0xc0000ac060
			slice1[1]=2, 地址=0xc0000ac068
			slice1[2]=3, 地址=0xc0000ac070
			slice1[3]=4, 地址=0xc0000ac078
			slice1[4]=5, 地址=0xc0000ac080
		*/
	}

	var slice2 []int

	// slice2 只引用 array1 的第二个到第三个元素（下标1到下标2），左闭右开的形式展现
	slice2 = array1[1:3]
	// array1 的值 [1,2,3,4,5]
	fmt.Println(slice2) // [2 3]
	// 打印下地址
	for i := 0; i < len(slice2); i++ {
		fmt.Printf("slices[%d]=%d,地址=%p\n", i, slice2[i], &slice2[i])
		/*
			slices[0]=2,地址=0xc0000ac068
			slices[1]=3,地址=0xc0000ac070
			结果和原数组完全一样，值和地址都没有随着切片而改变
		*/
	}

	// 修改切片的值同样会造成原来的数组的值改变
	// 因此为引用类型
	slice2[0] = 100

	fmt.Println(array1) // [1 100 3 4 5]
	fmt.Println(slice2) // [100 3]

	// 切片的第二种第一方式 make
	// make 分别设定类型，长度和容量
	var slice3 []int = make([]int, 5, 6)

	fmt.Printf("slice3的长度=%d,容量=%d,切片指向的底层数组的地址=%p,切片自己的地址=%p\n",
		len(slice3), cap(slice3), slice3, &slice3)
	//slice3的长度=5,容量=6,切片指向的底层数组的地址=0xc0000142a0,切片自己的地址=0xc00000c060

	// append
	slice3 = append(slice3, 7)
	// 给切片追加了一个元素 7
	fmt.Println(slice3) // [0 0 0 0 0 7]
	fmt.Printf("slice3追加之后的长度=%d,容量=%d\n", len(slice3), cap(slice3))
	// slice3追加之后的长度=6,容量=6

	// 在切片已经满了的情况下，再次追加
	slice3 = append(slice3, 8)
	fmt.Println(slice3)
	fmt.Printf("slice3追加之后的长度=%d,容量=%d，切片指向的底层数组的地址=%p\n", len(slice3), cap(slice3), slice3)
	// [0 0 0 0 0 7 8]
	// slice3追加之后的长度=7,容量=12，切片指向的底层数组的地址=0xc00008c060
	// 切片在检测到长度大于容量时，直接将容量扩容一倍,同时切片指向的底层数组也发生了改变
	// 切片是动态的数组，当容量超出原先定义的容量时，会在内存中重新的申请一块内存在存放数据，迁移了数据

	// 切片的拷贝：copy
	slice4 := []int{1, 2, 3, 4, 5}
	slice5 := make([]int, 10)
	// copy(), 第一个参数是目标切片，第二个参数是源切片
	fmt.Println(slice4)
	fmt.Println(slice5)
	// [1 2 3 4 5]
	// [0 0 0 0 0 0 0 0 0 0]
	copy(slice5, slice4)
	fmt.Println(slice4)
	fmt.Println(slice5)
	// [1 2 3 4 5]
	// [1 2 3 4 5 0 0 0 0 0]

	slice6 := make([]int, 1)
	copy(slice6, slice4)
	fmt.Println(slice6) // [1]
	// 因为 slice6 只有一个元素，容量和长度都是1，所以拷贝时也只拷贝一个

	// 切片是引用类型
	changeSlice(slice5)
	fmt.Println(slice5) // [100 2 3 4 5 0 0 0 0 0]
	// 不同于数组
}

func changeSlice(slice []int) {
	slice[0] = 100
	fmt.Println(slice) // [100 2 3 4 5 0 0 0 0 0]
}
